package com.shopping.mall.core.module.admin.service.impl;


import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.shopping.mall.auth.VO.AdminVo;
import com.shopping.mall.auth.VO.UserVo;
import com.shopping.mall.auth.exception.RedisTokenErrorException;
import com.shopping.mall.auth.exception.RedisTokenNullException;
import com.shopping.mall.auth.utils.JwtUtils;
import com.shopping.mall.auth.utils.UserUtils;
import com.shopping.mall.base.redis.RedisHeader;
import com.shopping.mall.core.module.admin.entity.Admin;
import com.shopping.mall.core.module.admin.entity.AdminLevel;
import com.shopping.mall.core.module.admin.mapper.AdminMapper;
import com.shopping.mall.core.module.admin.service.AdminService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.shopping.mall.base.http.ResponseState;
import com.shopping.mall.base.http.Result;
import com.shopping.mall.base.util.WrapperUtils;
import com.shopping.mall.core.module.user.entity.User;
import com.shopping.mall.core.module.user.mapper.UserMapper;
import io.jsonwebtoken.ExpiredJwtException;
import org.slf4j.Logger;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * Admin服务实现类
 * </p>
 *
 * @author zzy
 * @since 2021-08-09
 */
@Validated
@Service
public class AdminServiceImpl extends ServiceImpl<AdminMapper, Admin> implements AdminService {

    @Autowired
    private Logger logger;
    @Autowired
    private AdminMapper adminMapper;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private PasswordEncoder passwordEncoder;
    @Autowired
    private JwtUtils jwtUtils;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 查询个人信息
     *
     * @return
     */
    @Override
    public Result<Object> adminInfo() {
        final String loginUserId = UserUtils.getLoginUserId();
        return adminInfo(loginUserId);
    }

    /**
     * 超级管理员查询管理员信息
     *
     * @param id
     * @return
     */
    @Override
    public Result<Object> adminInfo(String id) {
        QueryWrapper<Admin> queryWrapper = new QueryWrapper<>();
        queryWrapper.select(Admin.class,i -> !i.getColumn().equals("password")).eq("id", id);
        final List<Admin> adminInfo = adminMapper.selectList(queryWrapper);
        return Result.OK(adminInfo);
    }

    /**
     * 修改个人信息
     *
     * @param adminVo
     * @return
     */
    @Override
    public Result<Object> changeAdminInfo(AdminVo adminVo) {
        final String loginUserId = UserUtils.getLoginUserId();
        Admin admin = new Admin();
        BeanUtils.copyProperties(adminVo, admin);
        admin.setId(loginUserId);
        adminMapper.updateById(admin);
        return Result.OK();
    }

    /**
     * 超级管理员修改管理员信息
     *
     * @param adminVo
     * @return
     */
    @Override
    public Result<Object> changeAdminInfoBySuper(AdminVo adminVo) {
        final Admin admin = adminMapper.selectById(adminVo.getId());
        if (admin.getLevel() == 1) {
            //不能更改超级管理员的用户信息
            return Result.error(ResponseState.WITHOUT_PERMISSION.getValue(), ResponseState.WITHOUT_PERMISSION.getMessage());
        }
        if (adminVo.getPassword()!=null){
            adminVo.setPassword(passwordEncoder.encode(adminVo.getPassword()));
        }
        BeanUtils.copyProperties(adminVo, admin);
        adminMapper.updateById(admin);
        return Result.OK();
    }

    /**
     * 登录
     *
     * @param adminVo
     * @return
     */
    @Override
    public Result<Object> login(AdminVo adminVo) {
        QueryWrapper<Admin> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("username", adminVo.getUsername());
        final Admin selectOne = adminMapper.selectOne(queryWrapper);
        if (selectOne == null) {
            logger.info("用户不存在,检查用户名或密码是否正确");
            return Result.error(ResponseState.PASSWORD_IS_ERROR.getValue(), ResponseState.PASSWORD_IS_ERROR.getMessage());
        }
        if (!passwordEncoder.matches(adminVo.getPassword(), selectOne.getPassword())) {
            logger.info("密码错误");
            return Result.error(ResponseState.PASSWORD_IS_ERROR.getValue(), ResponseState.PASSWORD_IS_ERROR.getMessage());
        }
        if (selectOne.getState() == 0) {
            logger.info("用户已封禁");
            return Result.error(ResponseState.ACCOUNT_IS_ILLEGAL.getValue(), ResponseState.ACCOUNT_IS_ILLEGAL.getMessage());
        }
        if (selectOne.getLevel()==1){
            selectOne.setAdminLevel(AdminLevel.SUPER_ADMIN);
        }
        if (selectOne.getLevel()==2){
            selectOne.setAdminLevel(AdminLevel.NORMAL_ADMIN);
        }
        final String accessToken = jwtUtils.getAccessToken(selectOne.getId(), selectOne.getAdminLevel().getLevel());
        final String refreshToken = jwtUtils.getRefreshToken(selectOne.getId(), selectOne.getAdminLevel().getLevel());
        stringRedisTemplate.opsForValue().set(RedisHeader.TOKEN.getHeader() + selectOne.getId(), accessToken, 24 * 3600L, TimeUnit.SECONDS);
        stringRedisTemplate.opsForValue().set(RedisHeader.REFRESH_TOKEN.getHeader() + selectOne.getId(), refreshToken, 15 * 24 * 3600L, TimeUnit.SECONDS);
        selectOne.setPassword(null);
        Map<String, Object> data = new HashMap<>();
        data.put("token", accessToken);
        data.put("refreshToken", refreshToken);
        data.put("admin", selectOne);
        return Result.OK(data);

    }

    /**
     * 退出登录
     *
     * @return
     */
    @Override
    public Result<Object> logout() {
        String loginUserId = UserUtils.getLoginUserId();
        stringRedisTemplate.delete(RedisHeader.TOKEN.getHeader() + loginUserId);
        stringRedisTemplate.delete(RedisHeader.REFRESH_TOKEN.getHeader() + loginUserId);
        logger.info("退出登录,redis token已删除");
        return Result.OK();
    }

    /**
     * 刷新token
     *
     * @param refreshToken: refreshToken
     * @return Result<Object>
     */
    @Override
    public Result<Object> refreshToken(String refreshToken) {
        try {
            final String id = jwtUtils.getUserId(refreshToken);
            final String adminLevel = jwtUtils.getUserRole(refreshToken);
            final String newAccessToken = jwtUtils.getAccessToken(id, adminLevel);
            final String newRefreshToken = jwtUtils.getRefreshToken(id, adminLevel);
            stringRedisTemplate.opsForValue().set(RedisHeader.TOKEN.getHeader() + id, newAccessToken, 24 * 3600L, TimeUnit.SECONDS);
            stringRedisTemplate.opsForValue().set(RedisHeader.REFRESH_TOKEN.getHeader() + id, newRefreshToken, 15 * 24 * 3600L, TimeUnit.SECONDS);
            final Admin admin = adminMapper.selectById(id);
            admin.setPassword(null);
            logger.info("refreshToken刷新成功");
            Map<String, Object> data = new HashMap<>();
            data.put("token", newAccessToken);
            data.put("refreshToken", newRefreshToken);
            data.put("admin", admin);
            return Result.OK(data);
        } catch (RedisTokenErrorException e) {
            logger.warn("refreshToken错误:{}",e.getStackTrace());
            return Result.error(ResponseState.REFRESH_TOKEN_IS_ERROR.getValue(), ResponseState.REFRESH_TOKEN_IS_ERROR.getMessage());
        } catch (RedisTokenNullException | ExpiredJwtException e) {
            logger.warn("refreshToken已过期:{}",e.getStackTrace());
            return Result.error(ResponseState.REFRESH_TOKEN_IS_EXPIRED.getValue(), ResponseState.REFRESH_TOKEN_IS_EXPIRED.getMessage());
        } catch (Exception e) {
            logger.error("发生错误:{}", e.getStackTrace());
            return Result.error(ResponseState.REFRESH_TOKEN_IS_ERROR.getMessage(), ResponseState.REFRESH_TOKEN_IS_ERROR.getValue());
        }
    }

    /**
     * 添加管理员
     *
     * @param adminVo
     * @return
     */
    @Override
    public Result<Object> add(AdminVo adminVo) {
        QueryWrapper<Admin> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("username", adminVo.getUsername());
        if (adminMapper.selectOne(queryWrapper) == null) {
            Admin admin = new Admin();
            admin.setUsername(adminVo.getUsername());
            admin.setPassword(passwordEncoder.encode(adminVo.getPassword()));
            admin.setLevel(2);//默认2级普通管理员
            admin.setState(1);
            adminMapper.insert(admin);
            logger.info("添加成功:{}", admin);
            return Result.OK();
        } else {
            logger.info("用户名已存在");
            return Result.error(ResponseState.USERNAME_IS_EXIST.getValue(), ResponseState.USER_NOT_EXIST.getMessage());
        }
    }

    /**
     * 分页查询
     *
     * @param current：页码
     * @param size：每页条数
     * @param admin：筛选条件
     * @return IPage<Admin>
     */
    @Override
    public IPage<Admin> getAdminPage(Integer current, Integer size, Admin admin) {
        QueryWrapper<Admin> queryWrapper = WrapperUtils.queryWrapper(admin);
        Page<Admin> adminPage = new Page<>(current, size);
        IPage<Admin> adminIPage = adminMapper.selectPage(adminPage, queryWrapper);
        return adminIPage;
    }

    /**
     * 查询所有
     *
     * @param admin：筛选条件
     * @return List<Admin>
     */
    @Override
    public List<Admin> getAll(Admin admin) {
        QueryWrapper<Admin> queryWrapper = WrapperUtils.queryWrapper(admin);
        return adminMapper.selectList(queryWrapper);
    }

    /**
     * 通过id删除
     *
     * @param id：adminId
     * @return Result<Object>
     */
    @Override
    public Result<Object> deleteById(String id) {
        Admin admin = adminMapper.selectById(id);
        if (admin == null) {
            return Result.error(ResponseState.TARGET_NOT_EXIST.getValue(), ResponseState.TARGET_NOT_EXIST.getMessage());
        }
        if (admin.getLevel() == 1) {
            return Result.error(ResponseState.WITHOUT_PERMISSION.getValue(), ResponseState.WITHOUT_PERMISSION.getMessage());
        }
        adminMapper.deleteById(id);
        return Result.OK();
    }

    /**
     * 批量删除
     *
     * @param ids：id列表
     * @return Result<Object>
     */
    @Override
    public Result<Object> batchDelete(List<String> ids) {
        UpdateWrapper<Admin> updateWrapper = new UpdateWrapper<>();
        updateWrapper.in("id", ids);
        adminMapper.delete(updateWrapper);
        return Result.OK();
    }

    /**
     * 用户列表
     *
     * @param current : 页码
     * @param size    : 一页的大小
     * @return
     */
    @Override
    public Result<Object> userList(Integer current, Integer size) {
        Page<User> page = new Page<>(current, size);
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.select(User.class,i -> !i.getColumn().equals("password"));
        final IPage<User> userIPage = userMapper.selectPage(page, queryWrapper);
        return Result.OK(userIPage);
    }

    /**
     * 管理员修改用户信息
     *
     * @param userVo
     * @return
     */
    @Override
    public Result<Object> changeUserInfoByAdmin(UserVo userVo) {
        User user = new User();
        BeanUtils.copyProperties(userVo, user);
        if (userVo.getPassword()!=null){
            user.setPassword(passwordEncoder.encode(userVo.getPassword()));
        }
        final int i = userMapper.updateById(user);
        logger.info("用户信息更新,有{}行被修改", i);
        return Result.OK();
    }
}